//---------------------------------------------------------------------------------------------------------------------------------------------------
// <copyright file="ControllerManager.cs" company="DarkWynter Studios">
//     Copyright (C)2007 DarkWynter Studios.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------------------------------------------------------------------------------------
// {Contact : darkwynter.com for licensing information
//---------------------------------------------------------------------------------------------------------------------------------------------------

// C# FAQ: How create an instance of a type using only its name
// http://en.csharp-online.net/CSharp_FAQ:_How_create_an_instance_of_a_type_using_only_its_name

namespace DarkWynter.Engine.Controllers
{
    #region Using Statements
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Input;
    using System.Diagnostics;

    using Microsoft.Xna.Framework.Graphics;
    using System.Reflection;
    #endregion

    /// <summary>
    /// Event Publisher
    /// ControllerManager requires a list of Types, which point to children of Controller
    /// It creates a child controller for each Type, for each controller plugged into the system.
    /// Use GetControllers to retrieve all the controllers of a particular type or a particular player.
    /// </summary>
    public class ControllerManager
    {
        private List<Controller> controllers;

        private List<Type> controllerTypes;
        private Type controllerTypeCurrent;

        public int PlayerCount;

        /// <summary>
        ///  Width and height of window required for mouse to function properly.
        /// </summary>
        public static Point clientSize;

        /// <summary>
        /// Player must add a new controller and reset viewports.
        /// </summary>
        /// <param name="args">Controller boolean event arguments</param>
        public delegate Controller OnControllerAdded(int playerNumber);
        public static event OnControllerAdded onControllerAdded;

        /// <summary>
        /// Player must add a new controller and reset viewports.
        /// </summary>
        /// <param name="args">Controller boolean event arguments</param>
        public delegate Controller OnControllerRemoved(int playerNumber);
        public static event OnControllerRemoved onControllerRemoved;

        public ControllerManager(int screenWidth, int screenHeight, List<Type> childControllerTypes)
        {
            clientSize = new Point(screenWidth, screenHeight);
            controllerTypes = childControllerTypes;
            controllers = new List<Controller>();
            PlayerCount = 0;
        }

        /// <summary>
        /// Checks to see if a new controller has been plugged in
        /// </summary>
        /// <param name="objectLibrary">ObjectLibrary</param>
        public void Update()
        {
            // Retrieve a list of PlayerIndexs already connected
            List<PlayerIndex> alreadyConnected = ConnectedXBoxControllers();

            // Check XBox Controllers
            for (PlayerIndex i = PlayerIndex.One; i <= PlayerIndex.Four; i++)
            {
                // If List doesn't contain controller, create one.
                if (!alreadyConnected.Contains(i))
                {
                    CheckControllersXBOX(i);
                }
                CheckControllersPC(i);
            }

        }

        /// <summary>
        /// Use this method to get Controllers of a particular type.
        /// </summary>
        /// <param name="type"></param>
        public List<Controller> GetControllers(Type childControllerType)
        {
            List<Controller> retValues = new List<Controller>();
            for (int i = 0; i < controllers.Count; i++)
            {
                if (controllers[i].GetType() == childControllerType)
                {
                    retValues.Add(controllers[i]);
                }
            }
            return retValues;
        }

        ///// <summary>
        ///// Use this method to get Controllers from a particular player.
        ///// </summary>
        ///// <param name="type"></param>
        //public List<Controller> GetControllers(int playerNumber)
        //{
        //    // Keyboard and Xbox for playerNumber = 0;
        //    // Xbox for playerNumber 1-N
        //}

        private List<PlayerIndex> ConnectedXBoxControllers()
        {
            List<PlayerIndex> alreadyConnected = new List<PlayerIndex>();
            for (int i = 0; i < controllers.Count; i++)
            {
                // If XBox or Both Controller 
                if (controllers[i].playerControllerType != ControllerType.PC_ONLY)
                {
                    // If controller has been disconnected
                    if (!GamePad.GetState(controllers[i].playerIndex).IsConnected)
                    {
                        // Controller has been disconnected so remove it
                        controllers.RemoveAt(i);

                        // Invoke event to signal that a new controller was removed.
                        ControllerManager.onControllerRemoved(i);

                        // Update iterator, so our loop isn't thrown off
                        i--;
                    }
                    else
                    {
                        // Still connected so add to our list of already connected controllers
                        alreadyConnected.Add(controllers[i].playerIndex);
                    }
                }
            }

            return alreadyConnected;
        }
        private void CheckControllersPC(PlayerIndex i)
        {
            // PC must set up keyboard and mouse
#if !XBOX360
            // Setup Keyboard for player in case there are no XBox controllers connected
            if (PlayerCount == 0)
            {
                //// If we are player 1 then we also have pc controls
                if (controllers.Count == 0)
                {
                // Create a controller of each type
                    for (int typeCount = 0; typeCount < controllerTypes.Count; typeCount++)
                    {
                        // User Supplied Type Constructor
                        ConstructorInfo constructorInfo = controllerTypes[typeCount].GetConstructor(new Type[] { typeof(int) });

                        // Create Child Controller from Type Constructor.
                        object controllerObject = constructorInfo.Invoke(new object[] { 0 });

                        // Add to Controllers List
                        if (controllerObject is Controller)
                        {
                            controllers.Add(controllerObject as Controller);

                            // We have one player the keyboard player
                            PlayerCount++;
                        }
                        if (ControllerManager.onControllerAdded != null)
                        {
                            // Invoke event to signal that a new controller was added.
                            ControllerManager.onControllerAdded(PlayerCount);
                        }
                    }
                }
            }
            // Else If Player 1 has a controller make sure they are given the keyboard too
            else if (PlayerCount >= 1 && controllers[0].playerControllerType == ControllerType.XBOX_ONLY)
            {
                // Create a controller of each type
                for (int typeCount = 0; typeCount < controllerTypes.Count; typeCount++)
                {
                    // User Supplied Type Constructor
                    ConstructorInfo constructorInfo = controllerTypes[typeCount].GetConstructor(new Type[] { typeof(int), PlayerIndex.One.GetType() });

                    // Create Child Controller from Type Constructor.
                    object controllerObject = constructorInfo.Invoke(new object[] { 0, typeof(PlayerIndex) });

                    // Add to Controllers List
                    if (controllerObject is Controller)
                        controllers.Add(controllerObject as Controller);
                }
            }
#endif
        }
        private void CheckControllersXBOX(PlayerIndex i)
        {
            // Check if our gamepad is connected but not added to the list
            if (GamePad.GetState(i).IsConnected)
            {
                                // Create a controller of each type
                for (int typeCount = 0; typeCount < controllerTypes.Count; typeCount++)
                {

                    // User Supplied Type Constructor
                    ConstructorInfo constructorInfo = controllerTypes[typeCount].GetConstructor(new Type[] { typeof(int) });

                    // Create Child Controller from Type Constructor.
                    object controllerObject = constructorInfo.Invoke(new object[] { i });

                    // Add to Controllers List
                    if (controllerObject is Controller)
                    {
                        controllers.Add(controllerObject as Controller);

                        // Update number of players
                        PlayerCount++;
                    }
                    if (ControllerManager.onControllerAdded != null)
                    {
                        // Invoke event to signal that a new controller was added.
                        ControllerManager.onControllerAdded(PlayerCount);
                    }
                }
            }
        }
    }
}
